﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace LabManagement
{
    public partial class Launcher
    {
        private static XNamespace wa = "http://schemas.microsoft.com/windowsazure";
        private const string Thumbprint = "AAE98142E3DC18D68C6DE17969A8618F5650FC48";
        private const string SubscriptionId = "e7a3c780-5440-44d6-817a-d9bb0e926642";
        private const string Version = "2013-08-01";
        private const string ServiceName = "vimgtemplate";
        private const string DeploymentName = "MyDeployment";
        private const string DeploymentLabel = "MyVM";
        private const string DeploymentSlot = "production";
        private const string UserName = "abhishek";
        private const string RoleName = "ABHISHEK-PC";
        private const string SourceImage = "VImageTemplate1";
        private const string OSVhdPath = "https://portalvhdsjd3l5xg2czntm.blob.core.windows.net/vhds/TemplateImage-TemplateImage-2014-06-24.vhd";
        private static X509Certificate2 Certificate { get; set; }

        protected void Unnamed_Click(object sender, EventArgs e)
        {
            Certificate = GetStoreCertificate(Thumbprint);

            // Create the uri for the request
            string uriFormat = "https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments";
            Uri uri = new Uri(String.Format(uriFormat, SubscriptionId, ServiceName));

            string AdminUserName = "abhishek";
            string AdminPassword = "abcd.1234";

            // Create the request to add a new Virtual Machine deployment
            XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
            XDocument requestBody = new XDocument(
              new XDeclaration("1.0", "UTF-8", "no"),
              new XElement(wa + "Deployment",
                new XElement(wa + "Name", DeploymentName),
                new XElement(wa + "DeploymentSlot", DeploymentSlot),
                new XElement(wa + "Label", DeploymentLabel),
                new XElement(wa + "RoleList",
                  new XElement(wa + "Role",
                    new XElement(wa + "RoleName", RoleName),
                    new XElement(wa + "RoleType", "PersistentVMRole"),
                    new XElement(wa + "ConfigurationSets",
                        new XElement(wa + "ConfigurationSet",
                            new XElement(wa + "ConfigurationSetType", "WindowsProvisioningConfiguration"),
                             new XElement(wa + "ComputerName", "AbhishekPC"),
                              new XElement(wa + "AdminPassword", AdminPassword),
                               new XElement(wa + "AdminUsername", AdminUserName),
                               new XElement(wa + "EnableAutomaticUpdates", "true"),
                               new XElement(wa + "ResetPasswordOnFirstLogon", "false"))),
                    new XElement(wa + "OSVirtualHardDisk",
                      new XElement(wa + "SourceImageName", SourceImage),
                      new XElement(wa + "OS", "Windows"))))));

            // Submit the request and get the response
            HttpWebResponse response = InvokeRequest(uri, "POST", requestBody);

            HttpStatusCode statusCode = response.StatusCode;

            // download the RDP

            if (statusCode == HttpStatusCode.OK)
            {
                uri = new Uri(string.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deployments/{2}/roleinstances/{3}/ModelFile?FileType=RDP", SubscriptionId, ServiceName, DeploymentName, RoleName));

                response = InvokeRequest(uri, "GET", null);


            }

        }

        private static X509Certificate2 GetStoreCertificate(string thumbprint)
        {
            List<StoreLocation> locations = new List<StoreLocation> 
            { 
            StoreLocation.CurrentUser, 
            StoreLocation.LocalMachine 
            };

            foreach (var location in locations)
            {
                X509Store store = new X509Store("My", location);
                try
                {
                    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
                    X509Certificate2Collection certificates = store.Certificates.Find(
                      X509FindType.FindByThumbprint, thumbprint, false);
                    if (certificates.Count == 1)
                    {
                        return certificates[0];
                    }
                }
                finally
                {
                    store.Close();
                }
            }
            throw new ArgumentException(string.Format(
              "A Certificate with Thumbprint '{0}' could not be located.", thumbprint));
        }

        // Add the request headers and get the response from the request
        private static HttpWebResponse InvokeRequest(Uri uri, string method, XDocument requestBody)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            request.Method = method;
            request.Headers.Add("x-ms-version", Version);
            request.ClientCertificates.Add(Certificate);
            request.ContentType = "application/xml";

            if (requestBody != null)
            {
                using (Stream requestStream = request.GetRequestStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(
                      requestStream, System.Text.UTF8Encoding.UTF8))
                    {
                        requestBody.Save(streamWriter, SaveOptions.DisableFormatting);
                    }
                }
            }

            HttpWebResponse response;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
                string operationId = response.Headers["x-ms-request-id"];
                var operationStatus = GetOperationStatus(operationId);


            }
            catch (WebException ex)
            {
                response = (HttpWebResponse)ex.Response;
                if (ex is WebException && ((WebException)ex).Status == WebExceptionStatus.ProtocolError)
                {
                    WebResponse errResp = ((WebException)ex).Response;
                    using (Stream respStream = errResp.GetResponseStream())
                    {
                        StreamReader reader = new StreamReader(respStream);
                        string eresp = reader.ReadToEnd();

                        reader.Close();
                    }
                }
            }
            response.Close();
            return response;
        }

        private static XElement GetOperationStatus(string requestId)
        {
            string uriFormat = "https://management.core.windows.net/{0}" +
                "/operations/{1}";
            Uri uri = new Uri(String.Format(uriFormat, SubscriptionId, requestId));
            XDocument responseBody;
            InvokeRequest(uri, "GET", HttpStatusCode.OK, null, out responseBody);
            return responseBody.Element(wa + "Operation");
        }
        private struct OperationResult
        {
            // The status: InProgress, Failed, Succeeded, or TimedOut.
            public OperationStatus Status { get; set; }

            // The http status code of the requestId operation, if any.
            public HttpStatusCode StatusCode { get; set; }

            // The approximate running time for PollGetOperationStatus.
            public TimeSpan RunningTime { get; set; }

            // The error code for the failed operation.
            public string Code { get; set; }

            // The message for the failed operation.
            public string Message { get; set; }
        }
        private enum OperationStatus
        {
            InProgress,
            Failed,
            Succeeded,
            TimedOut
        }
        private static OperationResult PollGetOperationStatus(
                                    string requestId,
                                    int pollIntervalSeconds,
                                    int timeoutSeconds)
        {
            OperationResult result = new OperationResult();
            DateTime beginPollTime = DateTime.UtcNow;
            TimeSpan pollInterval = new TimeSpan(0, 0, pollIntervalSeconds);
            DateTime endPollTime = beginPollTime + new TimeSpan(0, 0, timeoutSeconds);

            bool done = false;
            while (!done)
            {
                XElement operation = GetOperationStatus(requestId);
                result.RunningTime = DateTime.UtcNow - beginPollTime;
                try
                {
                    // Turn the Status string into an OperationStatus value
                    result.Status = (OperationStatus)Enum.Parse(
                        typeof(OperationStatus),
                        operation.Element(wa + "Status").Value);
                }
                catch (Exception)
                {
                    throw new ApplicationException(string.Format(
                        "Get Operation Status {0} returned unexpected status: {1}{2}",
                        requestId,
                        Environment.NewLine,
                        operation.ToString(SaveOptions.OmitDuplicateNamespaces)));
                }

                switch (result.Status)
                {
                    case OperationStatus.InProgress:
                        Console.WriteLine(
                            "In progress for {0} seconds",
                            (int)result.RunningTime.TotalSeconds);
                        Thread.Sleep((int)pollInterval.TotalMilliseconds);
                        break;

                    case OperationStatus.Failed:
                        result.StatusCode = (HttpStatusCode)Convert.ToInt32(
                            operation.Element(wa + "HttpStatusCode").Value);
                        XElement error = operation.Element(wa + "Error");
                        result.Code = error.Element(wa + "Code").Value;
                        result.Message = error.Element(wa + "Message").Value;
                        done = true;
                        break;

                    case OperationStatus.Succeeded:
                        result.StatusCode = (HttpStatusCode)Convert.ToInt32(
                            operation.Element(wa + "HttpStatusCode").Value);
                        done = true;
                        break;
                }

                if (!done && DateTime.UtcNow > endPollTime)
                {
                    result.Status = OperationStatus.TimedOut;
                    done = true;
                }
            }

            return result;
        }

        private static string InvokeRequest(
            Uri uri,
            string method,
            HttpStatusCode expectedCode,
            XDocument requestBody,
            out XDocument responseBody)
        {
            responseBody = null;
            string requestId = String.Empty;

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            request.Method = method;
            request.Headers.Add("x-ms-Version", Version);
            request.ClientCertificates.Add(Certificate);
            request.ContentType = "application/xml";

            if (requestBody != null)
            {
                using (Stream requestStream = request.GetRequestStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(
                        requestStream, System.Text.UTF8Encoding.UTF8))
                    {
                        requestBody.Save(streamWriter, SaveOptions.DisableFormatting);
                    }
                }
            }

            HttpWebResponse response;
            HttpStatusCode statusCode = HttpStatusCode.Unused;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                // GetResponse throws a WebException for 4XX and 5XX status codes
                response = (HttpWebResponse)ex.Response;
            }

            try
            {
                statusCode = response.StatusCode;
                if (response.ContentLength > 0)
                {
                    using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
                    {
                        responseBody = XDocument.Load(reader);
                    }
                }

                if (response.Headers != null)
                {
                    requestId = response.Headers["x-ms-request-id"];
                }
            }
            finally
            {
                response.Close();
            }

            if (!statusCode.Equals(expectedCode))
            {
                throw new ApplicationException(string.Format(
                    "Call to {0} returned an error:{1}Status Code: {2} ({3}):{1}{4}",
                    uri.ToString(),
                    Environment.NewLine,
                    (int)statusCode,
                    statusCode,
                    responseBody.ToString(SaveOptions.OmitDuplicateNamespaces)));
            }

            return requestId;
        }

    }
}
